/**
 * @fileOverview 日期控件
 * @copyright (c) 2012 
 * @author jianjun.wang@condenast.com.cn
 * @version 1.0
 */
if(typeof CNC=="undefined" || !CNC){
	var CNC = {};
}
if(typeof CNC.ui=="undefined" || !CNC.ui){
	CNC.ui = {};
} 
(function(){	
	/**
	 * CNC日期控件
	 * @class CNC日期控件
	 * @param {string} selector  input或日期容器的id
	 * @param {object} config 配置
	    {<br/>
		date: (date) 显示该日期所在的月份，默认为当天,<br/>
		selected: (date) 选中日期，默认为null,<br/>
		startDay: (int) 可选[0-6]，默认为0，以周日开头,<br/>
		closable: (boolean) 在弹出情况下，点选日期后是否关闭，默认为true,<br/>
		popup: (boolean) 是否弹出显示，默认为true,<br/>
		format: (string) 日期格式，默认为 "yyyy-mm-dd",<br/>
		yearRange: (array[yearStart, yearEnd]) 年份下拉列表显示的年份区间，越少越好避免下拉列表过长不好选择，默认为[1990,2020]，显示1990年到2020年,<br/>
		disabledDays: (array) 不可选的星期，默认为null，[0, 6]表示周六、周日不可点,<br/>
		disabledDates: (array) 不可选日期，默认为null，[new Date("2011/08/02"), new Date("2011/08/05"), new Date("2011/08/23")]表示这三天不可选,<br/>
		triggerType: (string) 触发事件，可选"click"或"focus"，默认为"click",<br/>
		minDate: (date) 可选区间的最小日期，默认为null,<br/>
		maxDate: (date) 可选区间的最大日期，默认为null,<br/>
		onPicked: (function) 选择日期后的回调函数，传递当前选中的日期，默认为null  ,<br/> 
		onYearChanged: (function) 切换年份后的回调函数，传递切换后的年份，默认为null,<br/>
		onMonthCanged: (function) 切换月份后的回调函数，传递切换后的月份，默认为null,<br/>
	   }
	 */
	CNC.ui.Calendar = function(selector, config){
		this._init(selector, config);
	}

	CNC.ui.Calendar.prototype = {
		_init: function(selectorId, config){
			var self = this, con = Tools.$( selectorId );
			self.id = selectorId;

			self.config = {
				date: new Date(),      //默认显示该日期所在的月份，默认为当天
				selected: null,  //选中日期
				startDay: 0,     //默认以周日开头
				closable: true,  //在弹出情况下，点选日期后是否关闭
				popup: true,     //是否弹出
				format: "yyyy-mm-dd",    //日期格式
				yearRange: [1990,2020],  //显示的年份区间，越少越好，避免下拉列表过长不好选择
				disabledDays: null,  //[0, 6]			
				disabledDates: null, //[date, date]
				triggerType: "click",  //"click","focus"
				minDate: null,       //date
				maxDate: null,       //date
				onPicked: null,   
				onYearChanged: null,
				onMonthCanged: null
			}

			Tools.mix(self.config, config || {});

			if(!self.config.popup){
				self.con = con;
			}else{
				self.trigger = con;
				self.con = Tools.createElement("div");
				self.con.id = new Date().getTime();
				
				Tools.css(self.con, {display:"none", position:"absolute"});
				document.body.appendChild( self.con );
			}
			Tools.addClass(self.con, "cnc-cal-box");
			
			self._handleDate();
			self.selected = self.config.selected;
			//处理startDay 0=>0 1=>6 2=>5 3=>4 4=>3 5=>2 6=>1
			if(self.config.startDay){
				self.config.startDay = (7 - self.config.startDay) % 7;
			}

			self.render();
			self._bindEvent();
		},
		/**
		 * @ignore
		 * 绑定外框事件
		 */
		_bindEvent: function(){
			var self = this;

			if( !self.config.popup ){
				return ;
			}

			Event.on(document.body, "click", function(event){	
				var target = Event.getTarget(event);

				if(target.id == self.id){
					return ;
				}

				if(self.con.style.visibility  == 'hidden') return ;
				/*var inRegion = function(dot,r){
					if(dot[0]> r[0].x && dot[0]<r[1].x && dot[1] > r[0].y && dot[1] < r[1].y){
						return true;
					}else{
						return false;
					}
				};

				if(!inRegion([event.pageX,event.pageY],[
								{
									x:self.con.style.offsetLeft,
									y:self.con.style.offsetTop
								},
								{
									x:self.con.style.offsetLeft + self.con.style.offsetWidth,
									y:self.con.style.offsetTop + self.con.style.offsetHeight
								}])){
                    self.hide();
                }*/
				/**
				 * @ignore
				 */
				var contains = function(pp, cc) {   
					while(cc!=null && typeof(cc.tagName)!="undefind"){
						if(cc==pp){    
						   return true;
						}
						cc=cc.parentNode;
					}
					return false;   
				}

				if(!contains(self.con, target)){
					self.hide();
				}
			})
			
			Event.on(self.trigger, self.config.triggerType, function(event){
				Event.stop(event);
				self.toggle();			
			});
		},
		/**
		 * 切换显示隐藏
		 */
		toggle: function(){
			var self = this;
			if(self.con.style.display == "none"){
				self.show();
			}else{
				self.hide();
			}
		},
		/**
		 * 显示控件
		 */
		show: function(){
			var self = this, 
				offset = Tools.getOffsetPosition(self.trigger),
				l = offset.left, 
				t = offset.top + self.trigger.offsetHeight || window.getComputedStyle( self.trigger, null ).getPropertyValue( "height" );

			Tools.css(self.con, {left: l+"px", top: t+"px", display: ""});
		},
		/**
		 * 隐藏控件
		 */
		hide: function(){
			var self = this;
			self.con.style.display = "none";
			//隐藏年 月列表
			Tools.getElementsByClassName(self.con, "cnc-cal-year-list")[0].style.display = "none";
			Tools.getElementsByClassName(self.con, "cnc-cal-month-list")[0].style.display = "none";
		},
		/**
		 * @ignore
		 */
		render: function(selected){
			this._renderUI();
			this._bindUIEvent();
			return this;
		},
		/**
		 * @ignore
		 */
		_renderUI: function(){
			var self = this;
			self.html = [
				'<div class="cnc-cal-hd">',
					'<a href="javascript:void(0)" class="cnc-cal-prev" title="上月"><span>上月</span></a>',
					'<div class="cnc-cal-year">',
						'<span>{$year}年</span>',
						'<p class="cnc-cal-year-list" style="display:none">',
							self.createYearList(),
							/*<i>2001</i>*/
						'</p>',
					'</div>',
					'<div class="cnc-cal-month">',
						'<span>{$month}月</span>',
						'<p class="cnc-cal-month-list" style="display:none">',
							'<i>1</i><i>2</i><i>3</i><i>4</i><i>5</i><i>6</i>',
							'<i>7</i><i>8</i><i>9</i><i>10</i><i>11</i><i>12</i>',
						'</p>',
					'</div>',
					'<a href="javascript:void(0)" class="cnc-cal-next" title="下月"><span>下月</span></a>',
				'</div>',
				'<div class="cnc-cal-bd">',
					'<div class="cnc-cal-whd cnc-clear">',
						self._handleDayOffset(),
						/*<span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span class="cnc-cal-rest">六</span><span class="cnc-cal-rest">日</span>*/				
					'</div>',
					'<div class="cnc-cal-dbd cnc-clear">',
						self.createDaysHtml(),
						/*<a class="cnc-cal-null" href="javascript:void(0)">0</a>
						<a href="javascript:void(0)">1</a>
						<a class="cnc-cal-disabled" href="javascript:void(0)">8</a>
						<a class="cnc-cal-today" href="javascript:void(0)">18</a>
						<a class="cnc-cal-selected" href="javascript:void(0)">26</a>*/
					'</div>',
				'</div>'
			].join("");

			self.html = self.html.replace("{$year}", self.year);
			self.html = self.html.replace("{$month}", self.month + 1);
			self.con.innerHTML = self.html;
			
		},
		/**
		 * @ignore
		 * 绑定日期控件元素事件
		 */
		_bindUIEvent: function(){
			var self = this,
				yearBtn = Tools.getElementsByClassName(self.con, "cnc-cal-year")[0],
				yearText = yearBtn.firstChild,
				yearList = Tools.getElementsByClassName(self.con, "cnc-cal-year-list")[0],
				yearItems = yearBtn.getElementsByTagName("i"),

				monthBtn = Tools.getElementsByClassName(self.con, "cnc-cal-month")[0],
				monthText = monthBtn.firstChild,
				monthList = Tools.getElementsByClassName(self.con, "cnc-cal-month-list")[0],
				monthItems = monthBtn.getElementsByTagName("i");

			Event.on(yearBtn, "click", function(event){	
				Event.stop(event);

				if(yearList.style.display == "none"){
					yearList.style.display = "";
				}else{
					yearList.style.display = "none";
				}
				monthList.style.display = "none";
			})

			for(var i=0; i<yearItems.length; i++){
				Event.on(yearItems[i], "click", function(event){
					Event.stop(event);
					var target = Event.getTarget(event);
					var val = parseInt(target.innerHTML, 10);
					yearText.innerHTML = val;
					self.year = val;
					yearList.style.display = "none";
					self.render();

					if(Tools.isFunction( self.config.onYearChange )){					
						self.config.onYearChange( self.year );
					}
				})
				if(Tools.ie6){
					Event.on(yearItems[i], "mouseover", function(event){
						var target = Event.getTarget(event);
						target.className = "hover";
					})
					Event.on(yearItems[i], "mouseout", function(event){
						var target = Event.getTarget(event);
						target.className = "";
					})
				}
			}

			Event.on(monthBtn, "click", function(event){	
				Event.stop(event);
				if(monthList.style.display == "none"){
					
					if(self.config.popup){
						monthList.style.left = "115px";
						monthList.style.top = "23px";
					}else{
						monthList.style.left = (Tools.getOffsetPosition(monthBtn).left + 11)  + "px";
						monthList.style.top = (Tools.getOffsetPosition(monthBtn).top + 22)  + "px";
					}
					monthList.style.display = "";
				}else{
					monthList.style.display = "none";
				}
				yearList.style.display = "none";
			})

			for(var i=0; i<monthItems.length; i++){
				Event.on(monthItems[i], "click", function(event){
					Event.stop(event);
					var target = Event.getTarget(event);
					var val = parseInt(target.innerHTML, 10);
					monthText.innerHTML = val;
					self.month = val - 1;
					monthList.style.display = "none";
					self.render();

					if(Tools.isFunction( self.config.onMonthChange )){					
						self.config.onMonthChange( self.month+1 );
					}
				})
				if(Tools.ie6){
					Event.on(monthItems[i], "mouseover", function(event){
						var target = Event.getTarget(event);
						target.className = "hover";
					})
					Event.on(monthItems[i], "mouseout", function(event){
						var target = Event.getTarget(event);
						target.className = "";
					})
				}
			}

			var preBtn = Tools.getElementsByClassName(self.con, "cnc-cal-prev")[0],
				nextBtn = Tools.getElementsByClassName(self.con, "cnc-cal-next")[0];

			Event.on(preBtn, "click", function(event){
				Event.stop(event);
				self._monthMinus().render();
				yearText.innerHTML = self.year+"年";
				monthText.innerHTML = (self.month+1)+"月";
				//fire monthChange event
				if(Tools.isFunction( self.config.onMonthChange )){					
					self.config.onMonthChange( self.month+1 );
				}
			})

			Event.on(nextBtn, "click", function(event){
				Event.stop(event);
				self._monthAdd().render();
				yearText.innerHTML = self.year+"年";
				monthText.innerHTML = (self.month+1)+"月";
				if(Tools.isFunction( self.config.onMonthChange )){					
					self.config.onMonthChange( self.month+1 );
				}
			})

			var dbd = Tools.getElementsByClassName(self.con, "cnc-cal-dbd")[0];
			Event.on(dbd, "click", function(event){
				Event.stop(event);
				var target = Event.getTarget(event);
				if(Tools.hasClass(target, "cnc-cal-dbd") || Tools.hasClass(target, "cnc-cal-null") || Tools.hasClass(target, "cnc-cal-disabled")){
					return false;
				}

				var selectedDate = parseInt(target.innerHTML, 10);

				//如果当天是30日或者31日，设置2月份就会出问题
				var d = new Date("2011/01/01");
				d.setDate(selectedDate);
				d.setYear(self.year);
				d.setMonth(self.month);

				self.selected = d;
				if(self.trigger && self.trigger.tagName.toUpperCase()=="INPUT" && self.trigger.type == "text"){
					self.trigger.value = DF.format(d, self.config.format);
				}
				if(Tools.isFunction( self.config.onPicked )){					
					self.config.onPicked(d);
				}
				if(self.config.popup && self.config.closable){
					self.hide();
				}
				self.render();
			})
		
		},
		/**
		 * @ignore
		 */
		_monthAdd: function(){
			var self = this;
			if(self.month == 11){
				self.month = 0;
				self.year++;
			}else{
				self.month++;
			}
			self.date = new Date(self.year + "/" + (self.month+1) + "/" + "01");
			return this;
		},
		/**
		 * @ignore
		 */
		_monthMinus: function(){
			var self = this;
			if(self.month == 0){
				self.month = 11;
				self.year--;
			}else{
				self.month--;
			}
			self.date = new Date(self.year + "/" + (self.month+1) + "/" + "01");
			return this;
		},
		/**
		 * @ignore
		 */
		_computeNextMonth: function(a){
			var year = a[0],
				month = a[1];
			if( month == 11 ){
				year++;
				month = 0;
			}else{
				month++;
			}
			return [year, month];
		},
		/**
		 * @ignore
		 */
		_computePreviousMonth: function(){
		
		},
		/**
		 * @ignore
		 */
		_handleDate: function(){
            var self = this,
            date = self.config.date;
            self.weekday = date.getDay() + 1;   //星期几 //指定日期是星期几
            self.day = date.getDate();   //几号
            self.month = date.getMonth() ;//月份
            self.year = date.getFullYear();//年份
            return this;
		},
		/**
		 * @ignore
		 */
		_handleDayOffset: function(){
			var self = this,
				days = ["日","一","二","三","四","五","六"],
				teml = "<span>{$day}</span>",
				offset = self.config.startDay,
				dayHtml = "";

			for(var i=0; i<7; i++){
				dayHtml += teml.replace("{$day}", days[ (i - offset + 7) % 7 ]);
			}
			
			return dayHtml;
		},
		/**
		 * @ignore
		 * 获取月份的天数
		 */
		_getNumOfDays: function(year, month){
			return 32 - new Date(year, month - 1, 32).getDate();
		},
		/**
		 * @ignore
		 */
		createDaysHtml: function(){
			var self = this,
				cfg = self.config,
				html = "",
				//计算本月第一天是星期几
				startWeekDay = (new Date(self.year+"/" + (self.month+1) + "/"+"01").getDay() + cfg.startDay + 7)%7,
				k = self._getNumOfDays(self.year, self.month + 1) + startWeekDay,
				d = new Date(),
				i, j;

			var disableDatesStr = "";
			for(i=0; cfg.disabledDates && i<cfg.disabledDates.length; i++){
				disableDatesStr += DF.format(cfg.disabledDates[i], "isoDate") + ",";
			}

			for(i=0; i<k; i++){
				j = i - startWeekDay + 1;
				if(i<startWeekDay){
					html += '<a class="cnc-cal-null" href="javascript:void(0)">0</a>';
				}else if(cfg.minDate instanceof Date && new Date(self.year+"/" + (self.month+1) + "/"+ j ).getTime() < cfg.minDate.getTime() ){
					html += '<a class="cnc-cal-disabled" href="javascript:void(0)">'+ j +'</a>';
				}else if(cfg.maxDate instanceof Date && new Date(self.year+"/" + (self.month+1) + "/"+ j ).getTime() > cfg.maxDate.getTime() ){
					html += '<a class="cnc-cal-disabled" href="javascript:void(0)">'+ j +'</a>';
				}else if( cfg.disabledDays && Tools.contains(cfg.disabledDays.join(""), new Date(self.year+"/" + (self.month+1) + "/"+ j).getDay()+"" ) ){//周末不可选
					html += '<a class="cnc-cal-disabled" href="javascript:void(0)">'+ j +'</a>';

				}else if( cfg.disabledDates && Tools.contains(disableDatesStr, DF.format(new Date(self.year+"/" + (self.month+1) + "/"+ j ), "isoDate")) ){
					html += '<a class="cnc-cal-disabled" href="javascript:void(0)">'+ j +'</a>';
				
				}else if( j == d.getDate() && 
					d.getFullYear() == self.year &&
					d.getMonth() == self.month ){ //today
					html += '<a class="cnc-cal-today" href="javascript:void(0)">'+ j +'</a>';
				}else if( self.selected && j == self.selected.getDate() && 
					self.year == self.selected.getFullYear() &&
					self.month == self.selected.getMonth()){
					html += '<a class="cnc-cal-selected" href="javascript:void(0)">'+ j +'</a>';

				}else{
					html += '<a href="javascript:void(0)">'+ j +'</a>';
				}
			}
			
			if(k%7!==0){
				for(i = 0; i < (7 - k % 7); i++){
					html += '<a class="cnc-cal-null" href="javascript:void(0)">0</a>';
				}
			}

			return html;
		},
		/**
		 * @ignore
		 */
		createYearList: function(){
			var range = this.config.yearRange, html = "";
			for(var i = range[0]; i <= range[1]; i++){				
				html += "<i>"+ i +"</i>"
			}
			return html;
		}
		
	};

	var Event = {		
		on:function(ele, evt, fcn){
		  if (Tools.isDefined(ele, "attachEvent"))
			  ele.attachEvent("on"+evt, function(event) { fcn(event); });
		
		  else if (Tools.isDefined(ele, "addEventListener"))
		    ele.addEventListener(evt, fcn, false);
		  return [ele,evt,fcn];
		},
		
		remove:function(args){
		  if (Tools.isDefined(args[0], "detachEvent"))
			  args[0].detachEvent("on"+args[1], args[2]);	
		  else if (Tools.isDefined(args[0], "removeEventListener")){
		    args[0].removeEventListener(args[1], args[2], false);
		  }
		},
		//on(ele, evt, bindAsEvent(object,method,param))
		//绑定事件注册，可传递参数
        bindAsEvent:function(object,method,param){
			return function(event){
				method.apply(object,[event||window.event].concat(param));
			}
		},
		
		//获取event对象目标元素
		getTarget:function(event){
			if (event == null)
				return null;
		
			if (typeof(event.srcElement) != "undefined")
				return event.srcElement;
			else
				return event.target;
		},
		//停止事件冒泡
		stop:function(evt){
		  if(document.all){
				  evt.returnValue = false;
				  evt.cancelBubble = true;
			}
			else {
					evt.preventDefault();
					evt.stopPropagation();
		  }
		},
		pageXY : function(event){
			var l = event.pageX || (event.clientX +(document.documentElement.scrollLeft || document.body.scrollLeft));
			var t = event.pageY || (event.clientY +(document.documentElement.scrollTop || document.body.scrollTop));
			return {left:l,top:t};
		}
	};
	var userAgent = navigator.userAgent.toLowerCase();
	var Tools = {
		ie : /*@cc_on!@*/false ,
		ie6 : navigator.appVersion.indexOf("MSIE 6.0") != -1,
		isWebkit : /applewebkit/.test( userAgent ) ,
		$ : function(id,docObj){
			if(!docObj){
				var doc = document;
		  }
			return doc.getElementById(id);
		},
		getElementsByClassName: function(obj, name){
			var checkNode = function(node,cname,arr){  
				 if(node.hasChildNodes()){  
					 var obj=node.childNodes;  
					 for(var i=0;i<obj.length;checkNode(obj[i],cname,arr),i++);  
				 }  
			   
				 try{  
					 if(node.className!=null){  
						 var cnames=node.className.split(' ');   
						 for(var i=0; i<cnames.length; (cnames[i]==cname) ? arr.push(node) : null, i++) ;  
					 }  
				 }catch(err){}
			 } 
			var arr = [];
			checkNode(obj, name, arr);  
			return arr;  
		},
		isDefined:function(obj, field){
			return typeof (obj[field]) != "undefined";
		},
		isFunction: function(obj) {
			return !!(obj && obj.constructor && obj.call && obj.apply);
		},
		createElement:function(tName,docObj){
			if(!docObj){
				var doc = document;
		  }
			return doc.createElement(tName);		
		},
		css : function( element, styles )
		{
			var style = element.style ;
			for ( var name in styles ){
				style[ name ] = styles[ name ] ;
			}
		},
		getOffsetPosition:function(elem){
			if ( !elem ) return {left:0, top:0};
			var top = 0, left = 0;
			if ( "getBoundingClientRect" in document.documentElement ){
				//jquery方法
				var box = elem.getBoundingClientRect(), 
				doc = elem.ownerDocument, 
				body = doc.body, 
				docElem = doc.documentElement,
				clientTop = docElem.clientTop || body.clientTop || 0, 
				clientLeft = docElem.clientLeft || body.clientLeft || 0,
				top  = box.top  + (self.pageYOffset || docElem && docElem.scrollTop  || body.scrollTop ) - clientTop,
				left = box.left + (self.pageXOffset || docElem && docElem.scrollLeft || body.scrollLeft) - clientLeft;
			}else{
				do{
					top += elem.offsetTop || 0;
					left += elem.offsetLeft || 0;
					elem = elem.offsetParent;
				} while (elem);
		  }
			return {left:left, top:top};	
		},
		trim : function(str){
			return str.replace(/(^\s*)|(\s*$)/g, "");
		},
		contains: function(str1,str2){
			//按小写匹配
			return str1.toLowerCase().indexOf(str2.toLowerCase()) > -1;
		},
		hasClass : function(ele,cls) {
            return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
		},
		addClass : function(ele,cls) {
            if (!this.hasClass(ele,cls)) ele.className += " "+cls;
		},		 
		removeClass :function(ele,cls) {
			if (this.hasClass(ele,cls)) {
				var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
				ele.className=ele.className.replace(reg,' ');
			}
		},
		mix: function(destination, sources){
			for(k in sources){
				if(sources[k] !== void 0){
					destination[k] = sources[k];
				}			
			}
			return destination;
		}
	};

	//Date Format 1.2.3
	function dateParse(data) {

		var date = null;

		//Convert to date
		if (!(date instanceof Date)) {
			date = new Date(data);
		}
		else {
			return date;
		}

		// Validate
		if (date instanceof Date && (date != "Invalid Date") && !isNaN(date)) {
			return date;
		}
		else {
			return null;
		}

	}

	/**
	 * @ignore
	 */
	var dateFormat = function(){
		var token = /w{1}|d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, timezoneClip = /[^-+\dA-Z]/g, pad = function(val, len){
			val = String(val);
			len = len || 2;
			while (val.length < len) {
				val = "0" + val;
			}
			return val;
		}, // Some common format strings
		masks = {
			"default": "ddd mmm dd yyyy HH:MM:ss",
			shortDate: "m/d/yy",
			//mediumDate:     "mmm d, yyyy",
			longDate: "mmmm d, yyyy",
			fullDate: "dddd, mmmm d, yyyy",
			shortTime: "h:MM TT",
			//mediumTime:     "h:MM:ss TT",
			longTime: "h:MM:ss TT Z",
			isoDate: "yyyy-mm-dd",
			isoTime: "HH:MM:ss",
			isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
			isoUTCDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",
			
			//added by jayli
			localShortDate: "yy年mm月dd日",
			localShortDateTime: "yy年mm月dd日 hh:MM:ss TT",
			localLongDate: "yyyy年mm月dd日",
			localLongDateTime: "yyyy年mm月dd日 hh:MM:ss TT",
			localFullDate: "yyyy年mm月dd日 w",
			localFullDateTime: "yyyy年mm月dd日 w hh:MM:ss TT"
		
		}, // Internationalization strings
		i18n = {
			dayNames: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
			monthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
		};
		
		// Regexes and supporting functions are cached through closure
		return function(date, mask, utc){
			var dF = dateFormat;
			
			// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
			if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
				mask = date;
				date = undefined;
			}
			
			// Passing date through Date applies Date.parse, if necessary
			date = date ? new Date(date) : new Date();
			if (isNaN(date)) {
				throw SyntaxError("invalid date");
			}
			
			mask = String(masks[mask] || mask || masks["default"]);
			
			// Allow setting the utc argument via the mask
			if (mask.slice(0, 4) == "UTC:") {
				mask = mask.slice(4);
				utc = true;
			}
			
			var _ = utc ? "getUTC" : "get", d = date[_ + "Date"](), D = date[_ + "Day"](), m = date[_ + "Month"](), y = date[_ + "FullYear"](), H = date[_ + "Hours"](), M = date[_ + "Minutes"](), s = date[_ + "Seconds"](), L = date[_ + "Milliseconds"](), o = utc ? 0 : date.getTimezoneOffset(), flags = {
				d: d,
				dd: pad(d),
				ddd: i18n.dayNames[D],
				dddd: i18n.dayNames[D + 7],
				w: i18n.dayNames[D + 14],
				m: m + 1,
				mm: pad(m + 1),
				mmm: i18n.monthNames[m],
				mmmm: i18n.monthNames[m + 12],
				yy: String(y).slice(2),
				yyyy: y,
				h: H % 12 || 12,
				hh: pad(H % 12 || 12),
				H: H,
				HH: pad(H),
				M: M,
				MM: pad(M),
				s: s,
				ss: pad(s),
				l: pad(L, 3),
				L: pad(L > 99 ? Math.round(L / 10) : L),
				t: H < 12 ? "a" : "p",
				tt: H < 12 ? "am" : "pm",
				T: H < 12 ? "A" : "P",
				TT: H < 12 ? "AM" : "PM",
				Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};
			
			return mask.replace(token, function($0){
				return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
			});
		}
	}();

    var DF = {
        format: function(date, mask, utc) {
            return dateFormat(date, mask, utc);
        },
        parse: function(date) {
            return dateParse(date);
        }
    };
})();

